Right-click on src to create a new folder:
Create related .c and .h files:
As shown below:
At this point, similar to GPIO operations, if there are custom header files, the file directory needs to be added to the path. The steps are as above, and the final result is as shown below:
x// init EEPROM deviceint eeprom_init(const char *i2c_dev) { int fd = open(i2c_dev, O_RDWR); if (fd < 0) { perror("Failed to open I2C bus"); return -1; }
if (ioctl(fd, I2C_SLAVE, EEPROM_ADDR) < 0) { perror("Failed to set I2C address"); close(fd); return -1; }
return fd;}
// Write data to EEPROMbool eeprom_write(int fd, unsigned short addr, const unsigned char *data, unsigned int len) { if (fd < 0 || data == NULL || len == 0) { return false; }
// Create a data buffer for writing unsigned char *buf = malloc(len + 2); if (buf == NULL) { perror("Memory allocation failed"); return false; }
// Set address (16-bit) buf[0] = (addr >> 8) & 0xFF; // High byte buf[1] = addr & 0xFF; // Low byte
// copy data memcpy(buf + 2, data, len);
// write data if (write(fd, buf, len + 2) != (len + 2)) { perror("EEPROM write failed"); free(buf); return false; }
free(buf); usleep(10000); // Wait for write completion return true;}
// Read data from EEPROMbool eeprom_read(int fd, unsigned short addr, unsigned char *data, unsigned int len) { if (fd < 0 || data == NULL || len == 0) { return false; }
// Set read address (16-bit) unsigned char addr_buf[2]; addr_buf[0] = (addr >> 8) & 0xFF; // High byte addr_buf[1] = addr & 0xFF; // Low byte
if (write(fd, addr_buf, 2) != 2) { perror("EEPROM set read address failed"); return false; }
// read data if (read(fd, data, len) != len) { perror("EEPROM read failed"); return false; }
return true;}
// Close EEPROM devicevoid eeprom_close(int fd) { if (fd >= 0) { close(fd); }}Right-click on src to create a new folder:
Create related .c and .h files, as shown below:
xxxxxxxxxx// init shtc3 sensor deviceint shtc3_init(const char *i2c_dev) { int fd = open(i2c_dev, O_RDWR); if (fd < 0) { perror("Failed to open I2C bus"); return -1; }
if (ioctl(fd, I2C_SLAVE, SHTC3_ADDR) < 0) { perror("Failed to open SHTC3 device"); close(fd); return -1; }
return fd;}
// Read data from SHTC3 sensorbool shtc3_read_data(int fd, shtc3_data_t *data) { unsigned char cmd[2]; unsigned char sensor_data[6];
if (fd < 0 || data == NULL) { return false; }
// Wake up sensor cmd[0] = (CMD_WAKEUP >> 8) & 0xFF; cmd[1] = CMD_WAKEUP & 0xFF; if (write(fd, cmd, 2) != 2) { perror("SHTC3 wakeup failed"); return false; } usleep(1000);
// Send measurement command cmd[0] = (CMD_MEASURE >> 8) & 0xFF; cmd[1] = CMD_MEASURE & 0xFF; if (write(fd, cmd, 2) != 2) { perror("SHTC3 measure command failed"); return false; } usleep(20000);
// Read 6 bytes of data if (read(fd, sensor_data, 6) != 6) { perror("Failed to read SHTC3 data"); return false; }
// Convert raw data uint16_t raw_temp = (sensor_data[0] << 8) | sensor_data[1]; uint16_t raw_hum = (sensor_data[3] << 8) | sensor_data[4];
// Calculate temperature and humidity data->temperature = -45 + 175 * ((float)raw_temp / 65535.0); data->humidity = 100 * ((float)raw_hum / 65535.0);
return true;}
// Close SHTC3 sensor devicevoid shtc3_close(int fd) { if (fd >= 0) { unsigned char cmd[2]; // Send sleep command cmd[0] = (CMD_SLEEP >> 8) & 0xFF; cmd[1] = CCMD_SLEEP & 0xFF; write(fd, cmd, 2); close(fd); }}
Right-click on src to create a new folder:
Create related .c and .h files, as shown below:
xxxxxxxxxx// use PCF8563 RTC or PCF85063 RTC
unsigned char dec2bcd(int dec) { return ((dec / 10) << 4) | (dec % 10);}
uint8_t bcd2dec(uint8_t bcd) { return ((bcd >> 4) * 10) + (bcd & 0x0F);}
int open_rtc_dev(const char *i2c_dev){ uint8_t buf[2]; // Register address + 1 data byte uint8_t slave_addr = PCF85063_SLAVE_ADDR; // Default slave address for PCF85063 int fd = open(i2c_dev, O_RDWR); if (fd < 0) { perror("Failed to open I2C bus"); return -1; }
slave_addr = PCF8563_DEVICE_ADDR; // Use PCF8563 address if defined
if (ioctl(fd, I2C_SLAVE, slave_addr) < 0) { perror("Failed to set I2C address"); close(fd); return -1; }
unsigned char reg = 0x04; if (fd < 0) { return false; }
// reset PCF8563 buf[0] = PCF8563_REG_CONTROL_STATUS1; buf[1] = 0x00; if (write(fd, buf, sizeof(buf)) != sizeof(buf)) { perror("set_rtc write failed"); return false; }
// disable PCF8563 alarm buf[0] = PCF8563_REG_CONTROL_STATUS2; buf[1] = 0x00; if (write(fd, buf, sizeof(buf)) != sizeof(buf)) { perror("set_rtc write failed"); return false; }
// Disable clock output buf[0] = PCF8563_REG_CLKOUT_CONTROL; buf[1] = 0x00; if (write(fd, buf, sizeof(buf)) != sizeof(buf)) { perror("set_rtc write failed"); return false; }
// disable timer buf[0] = PCF8563_REG_TIMER_CONTROL; buf[1] = 0x00; if (write(fd, buf, sizeof(buf)) != sizeof(buf)) { perror("set_rtc write failed"); return false; } // reset PCF85063 buf[0] = PCF85063_REG_CONTROL1; buf[1] = 0x00; if (write(fd, buf, sizeof(buf)) != sizeof(buf)) { perror("set_rtc write failed"); return false; }
// Disable the alarm function and timer interrupt buf[0] = PCF85063_REG_CONTROL2; buf[1] = 0x00; if (write(fd, buf, sizeof(buf)) != sizeof(buf)) { perror("set_rtc write failed"); return false; }
// Disable clock output buf[0] = PCF85063_REG_TIMER_MODE; buf[1] = 0x00; if (write(fd, buf, sizeof(buf)) != sizeof(buf)) { perror("set_rtc write failed"); return false; }
return fd;}
bool read_rtc(int fd, stDateTime *dt){ unsigned char reg = PCF85063_REG_SECONDS; // read from second register unsigned char data[7];
if (fd < 0 || dt == NULL) { return false; }
reg = PCF8563_REG_VL_SEC; // read from second register for PCF8563
// Set read start address (write 0x04 if (write(fd, ®, 1) != 1) { perror("set read register failed"); return false; }
// Read 7 bytes if (read(fd, data, 7) != 7) { perror("read rtc failed"); return false; }
dt->second = bcd2dec(data[0] & 0x7F); dt->minute = bcd2dec(data[1] & 0x7F); dt->hour = bcd2dec(data[2] & 0x3F); dt->day = bcd2dec(data[3] & 0x3F); dt->weekday = bcd2dec(data[4] & 0x07); dt->month = bcd2dec(data[5] & 0x1F); dt->year = bcd2dec(data[6]);
return true;}
bool set_rtc(int fd, const stDateTime *dt){ unsigned char reg = PCF85063_REG_SECONDS; if (fd < 0) { return false; }
reg = PCF8563_REG_VL_SEC; // read from second register for PCF8563
// Create a data buffer for writing uint8_t buf[8]; // Register header (1 byte) + 7-byte data field buf[0] = reg; // Starting register address for write buf[1] = dec2bcd(dt->second); buf[2] = dec2bcd(dt->minute); buf[3] = dec2bcd(dt->hour); buf[4] = dec2bcd(dt->day); buf[5] = dec2bcd(dt->weekday); buf[6] = dec2bcd(dt->month); buf[7] = dec2bcd(dt->year % 100);
// Write all data at once if (write(fd, buf, sizeof(buf)) != sizeof(buf)) { perror("set_rtc write failed"); return false; }
return true;}
void close_rtc_dev(int fd){ if (fd >= 0) { close(fd); // Close the incoming file descriptor }}
Open /home/mind/petalinux_projects/petalinux-mind/project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi, and add the following content:
xxxxxxxxxx&i2c0 {
clock-frequency = <100000>; rtc@51{ compatible="nxp,pcf8563"; reg=<0x51>; };};After making the changes, save and exit, then re-run the following steps:
xxxxxxxxxxpetalinux-buildpetalinux-package --boot --fsbl ./images/linux/zynq_fsbl.elf --u-boot --fpga --forceCopy the BOOT.BIN, boot.scr, and image.ub files from the project directory images -> linux to the FAT partition of the SD card. The file system remains unchanged. Restart the development board.
After the system restarts, check the /dev/ directory for any RTC-related device files, as shown below:
You can see there are two device files in the /dev directory: rtc and rtc0, where rtc is a soft link to rtc0. This rtc0 corresponds to our pcf8563 device. If no RTC devices are registered (using the Linux RTC device driver framework) in the system, there will be no rtc device files in the /dev directory. If multiple RTC devices are registered in the kernel, they will be named rtc0, rtc1, rtc2, etc. There is an rtc directory under /sys/class, and the files in this directory are as follows:
Here, rtc0 corresponds to the pcf8563 device.
(1) View time:
The date command shows the system clock, not the RTC clock. The system clock is maintained by the kernel after system startup, not by the RTC hardware. Use the hwclock command to view the RTC clock.
xxxxxxxxxxdatehwclock(2) Set RTC time:
After setting, use the date command again to see that the current time has been changed.
xxxxxxxxxxdate -s "2023-09-22 15:19:36"date(3) Write to RTC:
xxxxxxxxxxhwclock -wAt this point, the time will not be lost even after a system restart. If the development board has a coin cell battery attached, the time will not be lost even if the board is powered off.
root@petalinux-mind:~# dateThu Aug 21 13:46:37 UTC 2025